package com.ltmonitor.service;

import com.ltmonitor.entity.TerminalCommand;
import com.ltmonitor.entity.VehicleData;
import com.ltmonitor.util.Constants;
import com.ltmonitor.util.DateUtil;
import com.ltmonitor.video.entity.VideoRequest;
import com.ltmonitor.video.entity.VideoServerConfig;
import com.ltmonitor.vo.VideoRequestMessage;
import com.ltmonitor.web.vo.VideoConnection;
import com.ltmonitor.web.vo.VideoServerInfo;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 分析当前在线的视频请求，如果某一个请求没有停止播放，将下发停止播放的指令
 * 对当前的视频请求进行管理
 */
@Service("onlineVideoRequestService")
public class OnlineVideoRequestService implements IOnlineVideoRequestService {

    protected Logger logger = Logger.getLogger(OnlineVideoRequestService.class);
    /**
     * key是sessionId, Date是在线时间，用于判断用户是否关闭了视频播放器，是否需要停止视频播放
     */
    private ConcurrentMap<String, Date> onlineMap = new ConcurrentHashMap<String, Date>();

    private ConcurrentMap<String, VideoRequest> requestMap = new ConcurrentHashMap<String, VideoRequest>();

    private boolean isContinue = true;

    @Autowired
    private IVideoRequestService  videoRequestService;
    /**
     * 终端命令服务
     */
    @Autowired
    protected ITerminalCommandService terminalCommandService;
    /**
     * 车辆信息服务
     */
    @Autowired
    protected IVehicleDataService vehicleDataService;

    @Autowired
    protected IVideoServerConfigService videoServerConfigService;
    /**
     * spring jms 消息模板，发送实时数据给其他模块
     */
    @Resource(name = "jmsTemplate")
    private JmsTemplate jmsTemplate;


    // 队列名gzframe.demo
    @Resource(name = "videoRquestMessageQueueDestination")
    private Destination videoRquestMessageQueueDestination;


    /**
     * 开辟线程，定时更新实时数据入库
     */
    private Thread checkThread;

    /**
     * 记录服务器的视频流量信息
     */
    private VideoServerInfo videoServerInfo = new VideoServerInfo();

    /**
     * 自动启动
     */
    @PostConstruct
    public void init()
    {
        checkThread = new Thread(new Runnable() {
            public void run() {
                checkVideoSessionThreadFunc();
            }
        });
        checkThread.start();
    }

    private void checkVideoSessionThreadFunc()
    {
        while(isContinue)
        {
            try
            {
                checkVideoSession();
            }catch (Exception ex)
            {
                logger.error(ex.getMessage(),ex);
            }
            try
            {
               Thread.sleep(30000);
            }catch (Exception ex)
            {
                logger.error(ex.getMessage(),ex);
            }
        }
    }

    private void checkVideoSession()
    {
         Set<String> keys = onlineMap.keySet();
         for(String sessionId : keys)
         {
             Date onlineDate = onlineMap.get(sessionId);
             double interval = com.ltmonitor.util.DateUtil.getSeconds(onlineDate, new Date());
             if(interval > 90 )
             {
                 this.clearRequest(sessionId);
                 onlineMap.remove(sessionId);
             }
         }
    }

    /**
     * 获取当前的在线视频请求
     * @return
     */
    public List<VideoRequest> getOnlineVideoRequest()
    {
        ArrayList<VideoRequest> res = new ArrayList<VideoRequest>();
        return new ArrayList<>(requestMap.values());
    }

    public List<VideoRequest> getMyOwnOnlineVideoRequest(String sessionId)
    {
        ArrayList<VideoRequest> result = new ArrayList<>();
        Collection<VideoRequest> rs = this.requestMap.values();
        for(VideoRequest r: rs)
        {
            if(r.getSessionId().equals(sessionId))
            {
                result.add(r);
            }
        }
        return result;
    }


    public List<VideoRequest> getOnlineVideoRequest(String simNo, int channel)
    {
        ArrayList<VideoRequest> result = new ArrayList<>();
        Collection<VideoRequest> rs = this.requestMap.values();
        for(VideoRequest r: rs)
        {
            if(r.getSimNo().equals(simNo) && r.getChannelId() == channel)
            {
                result.add(r);
            }
        }
        return result;
    }


    @Override
    public void updateOnlineTime(String sessionId)
    {
        onlineMap.put(sessionId, new Date());
    }

    @Override
    public void removeVideoSession(String sessionId)
    {
        onlineMap.remove(sessionId);
    }

    /**
     * 将当前的视频请求保存在内存中，并将请求消息通过JMS消息传递给视频服务器
     * @param v
     */
    @Override
    public void updateVideoRequest(VideoRequest v) {
        final VideoRequestMessage vm = new VideoRequestMessage();
        vm.setChannelId(v.getChannelId());
        vm.setSimNo(v.getSimNo());
        vm.setMediaType(v.getMediaType());
        vm.setUserId(v.getUserId());
        sendJMSMessageToVideoServer(vm);

        String key = v.getRequestKey();
        if (requestMap.containsKey(key)) {
            VideoRequest r = this.requestMap.get(key);
            //this.videoRequestService.save(r);
        } else {
            requestMap.put(key, v);
            v.setStartTime(new Date());
            videoRequestService.save(v);
            //v.setFlow1(getDataUsage(v.getSimNo(),v.getChannelId()));
        }
    }

    /**
     * 通过JMS 给视频服务器发送请求消息
     * @param vm
     */
    private void sendJMSMessageToVideoServer(final VideoRequestMessage vm)
    {
        try {
            jmsTemplate.send(this.videoRquestMessageQueueDestination, new MessageCreator() {
                public Message createMessage(Session session) throws JMSException {

                    String jsonMsg = JSONObject.fromObject(vm).toString();
                    return session.createTextMessage(jsonMsg);
                }
            });
        }catch (Exception ex)
        {
            logger.error(ex.getMessage(),ex);
        }
    }

    @Override
    public TerminalCommand stopVideoRequest(long userId, String simNo, int channelId, String sessionId, int stream, int mediaType)
    {
        StringBuilder sb = new StringBuilder();
        sb.append(simNo).append("_").append(channelId).append("_").append(mediaType).append("_").append(sessionId);
        String key= sb.toString();

            final VideoRequestMessage vm = new VideoRequestMessage();
            vm.setChannelId(channelId);
            vm.setSimNo(simNo);
            vm.setMediaType(mediaType);
            vm.setUserId(userId);
            vm.setCommand(VideoRequestMessage.COMMAND_STOP);
            sendJMSMessageToVideoServer(vm);

        if(this.requestMap.containsKey(key))
        {
            VideoRequest vr = this.requestMap.remove(key);
            vr.setEndTime(new Date());
            vr.setStatus(VideoRequest.STATUS_END);
            this.videoRequestService.saveOrUpdate(vr);
        }


        if(isChannelOnRequest(simNo, channelId) == false )
        {
            return sendStopVideoRealPlayRequestCommand(simNo, channelId);
        }
        return null;
    }

    private VideoConnection getVideoConnection(String simNo, int channelId)
    {
        for(VideoConnection vc : this.videoServerInfo.getConnections())
        {
            if(vc.getSimNo().equals(simNo) && vc.getChannelId() == channelId)
                return vc;
        }
        return null;
    }

    public VideoServerInfo getVideoServerInfo()
    {
        return this.videoServerInfo;
    }

    public  void updateVideoServerInfo(VideoServerInfo vi)
    {
        this.videoServerInfo = vi;
        VideoServerConfig videoServerConfig = this.videoServerConfigService.getVideoServerConfig();
        //是否下发视频状态通知指令（部标检测所要求，实际不需要）
        boolean sendVideoServerState = videoServerConfig.isSendVideoChannelStatus();
        for(VideoConnection vc : vi.getConnections())
        {
            List<VideoRequest> ls = getOnlineVideoRequest(vc.getSimNo(), vc.getChannelId());
            for(VideoRequest vr : ls)
            {
                if(vr.getFlow1() == -1)
                {
                    vr.setFlow1(vc.getDataUsage());
                }
                vr.setFlow2(vc.getDataUsage());
            }
            vc.setUserCount(ls.size());
            Date createDate = DateUtil.stringToDatetime(vc.getCreateDate(), "yyyy-MM-dd HH:mm:ss");
            Date onlineDate = DateUtil.stringToDatetime(vc.getOnlineDate(), "yyyy-MM-dd HH:mm:ss");
            double minutes = DateUtil.getSeconds(createDate, onlineDate) / 60;
            vc.setTimeSpan(Constants.getIntervalDescr(minutes));
            VehicleData vd = this.vehicleDataService.getVehicleBySimNo(vc.getSimNo());
            if(vd != null)
            {
                vc.setPlateNo(vd.getPlateNo());
            }
            if(sendVideoServerState)
               SendVideoStateCommand(vc.getSimNo(), vc.getChannelId());
        }
    }

    /**
     * 定时对终端下发视频通道状态通知指令，部标1078必须要求
     * @param simNo
     * @param channelId
     */
    private void SendVideoStateCommand(String simNo, int channelId)
    {
        try {
            if (simNo.length() > 11)
                simNo = simNo.substring(1);
            VehicleData vd = this.vehicleDataService.getVehicleBySimNo(simNo);
            TerminalCommand tc = new TerminalCommand();
            tc.setPlateNo(vd.getPlateNo());
            tc.setSimNo(vd.getSimNo());
            tc.setVehicleId(vd.getEntityId());
            tc.setCmdType(0x9105);
            StringBuilder sb = new StringBuilder();
            sb.append(channelId).append(';').append(0);
            tc.setCmdData(sb.toString());

            this.terminalCommandService.save(tc);
        }catch (Exception ex)
        {
            logger.error(ex.getMessage(),ex);
        }
    }

    private boolean isChannelOnRequest(String simNo, int channelId)
    {
        Collection<VideoRequest> rs = this.requestMap.values();
        for(VideoRequest r: rs)
        {
            if(r.getSimNo().equals(simNo) && r.getChannelId() == channelId)
            {
                return true;
            }
        }
        return false;
    }

    /**
     * =清除本次会话的所有的视频请求
     * @param sessionId
     */
    public void clearRequest(String sessionId)
    {
        Set<String> keys = this.requestMap.keySet();
        for(String  key: keys)
        {
            VideoRequest v = this.requestMap.get(key);
            if(v != null && sessionId.equals(v.getSessionId()))
            {
                stopVideoRequest(v.getUserId(), v.getSimNo(), v.getChannelId(), v.getSessionId(),v.getStreamType(), v.getMediaType());
                this.requestMap.remove(key);
                if(isChannelOnRequest(v.getSimNo(), v.getChannelId()) == false)
                {
                    sendStopVideoRealPlayRequestCommand(v.getSimNo(), v.getChannelId());
                }
            }
        }


        for(VideoConnection vc : this.videoServerInfo.getConnections())
        {
            if(vc.getGovUserPlaying())
                continue;
            List<VideoRequest> ls = this.getOnlineVideoRequest(vc.getSimNo(), vc.getChannelId());
            if(ls.size() == 0)
            {
                sendStopVideoRealPlayRequestCommand(vc.getSimNo(), vc.getChannelId());
            }
        }

    }

    /**
     * 下发停止实时播放视频的指令
     * @param simNo
     * @param channelId
     */
    public TerminalCommand sendStopVideoRealPlayRequestCommand(String simNo, int channelId)
    {
        VideoConnection vc = this.getVideoConnection(simNo, channelId);
        if(vc != null && vc.getGovUserPlaying())
        {
            logger.error(simNo + "通道" + channelId + "正在被政府监管平台用户播放，无法停止");
            return null;
        }
        VehicleData vd = this.vehicleDataService.getVehicleBySimNo(simNo);
        if(vd == null)
        {
            logger.error(simNo+"找不到车辆信息");
            return null;
        }

        TerminalCommand tc = new TerminalCommand();
        tc.setPlateNo(vd.getPlateNo());
        tc.setSimNo(vd.getSimNo());
        tc.setVehicleId(vd.getEntityId());
        tc.setCmdType(0x9102);
        StringBuilder sb = new StringBuilder();
        byte controlCommand = 0;
        byte videoType  = 0;
        byte streamType = 0;
        sb.append(channelId).append(';').append(controlCommand).append(';').append(videoType).append(';').append(streamType);
        tc.setCmdData(sb.toString());

        this.terminalCommandService.save(tc);
        return tc;
    }

    /**
     * 下发停止播放视频的指令
     * @param simNo
     * @param channelId
     */
    public TerminalCommand sendStopVideoTalkCommand(String simNo, int channelId)
    {
        VehicleData vd = this.vehicleDataService.getVehicleBySimNo(simNo);
        if(vd == null)
        {
            logger.error(simNo+"找不到车辆信息");
            return null;
        }

        TerminalCommand tc = new TerminalCommand();
        tc.setPlateNo(vd.getPlateNo());
        tc.setSimNo(vd.getSimNo());
        tc.setVehicleId(vd.getEntityId());
        tc.setCmdType(0x9102);
        StringBuilder sb = new StringBuilder();
        byte controlCommand = 4;//关闭双向对讲
        byte videoType  = 0;
        byte streamType = 0;
        sb.append(channelId).append(';').append(controlCommand).append(';').append(videoType).append(';').append(streamType);
        tc.setCmdData(sb.toString());

        this.terminalCommandService.save(tc);
        return tc;
    }


    /**
     * 下发停止回放视频的指令
     * @param simNo
     * @param channelId
     */
    public TerminalCommand sendStopVideoPlayBackCommand(String simNo, int channelId)
    {
        VehicleData vd = this.vehicleDataService.getVehicleBySimNo(simNo);
        if(vd == null)
        {
            logger.error(simNo+"找不到车辆信息");
            return null;
        }

        TerminalCommand tc = new TerminalCommand();
        tc.setPlateNo(vd.getPlateNo());
        tc.setSimNo(vd.getSimNo());
        tc.setVehicleId(vd.getEntityId());
        tc.setCmdType(0x9202);
        StringBuilder sb = new StringBuilder();
        int playbackType = 2;//结束回放
        int speed = 0;
        sb.append(channelId).append(";").append(playbackType).append(";").append(speed).append(";")
                .append(com.ltmonitor.util.DateUtil.toStringByFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));
        tc.setCmdData(sb.toString());

        this.terminalCommandService.save(tc);
        return tc;
    }
}
